package com.gitee.wsl.ext.collection

import kotlin.jvm.JvmName

fun <T> Iterable<T?>.anyNull(): Boolean = any { it == null }

fun <T> Collection<T>?.isBlank(): Boolean = isNullOrEmpty()

fun <T> Collection<T?>.anyNull(): Boolean = any { it == null }

fun <T> Collection<T?>.allNull(): Boolean = all { it == null }

fun <T> Collection<T?>.countNulls(): Int = count { it == null }
fun <T> Collection<T?>.countNonNulls(): Int = size - countNulls()

fun <T : Any> Iterable<T?>.trimNulls(): List<T> = filterNotNull()
fun <T : Any> Iterable<T?>.trimNullsToMutableList(): MutableList<T> = filterNotNullTo(mutableListOf())


 fun <T : Comparable<T>> Iterable<T>.isSorted(): Boolean =
    isSortedBy { it }

inline fun <T, R : Comparable<R>> Iterable<T>.isSortedBy(crossinline selector: (T) -> R?): Boolean =
    isSortedWith(compareBy(selector))

fun <T> Iterable<T>.isSortedWith(comparator: Comparator<in T>): Boolean =
    asSequence().isSortedWith(comparator)

 fun <T : Comparable<T>> Iterable<T>.isSortedDescending(): Boolean =
    isSortedWith(reverseOrder())

inline fun <T, R : Comparable<R>> Iterable<T>.isSortedByDescending(crossinline selector: (T) -> R?): Boolean =
    isSortedWith(compareByDescending(selector))


inline fun <T, R> Iterable<T>.hasDuplicatesBy(selector: (T) -> R): Boolean {
    val registry = mutableListOf<R>()
    forEach {
        val sel = selector(it)
        if (registry.contains(sel)) {
            return true
        }
        registry.add(sel)
    }
    return false
}

fun <T> Iterable<T>.equalsByElement(other: Iterable<T>): Boolean {
    val iterator1 = iterator()
    val iterator2 = other.iterator()
    while (iterator1.hasNext() && iterator2.hasNext()) {
        if (iterator1.next() != iterator2.next()) return false
    }
    if (iterator1.hasNext() || iterator2.hasNext()) return false
    return true
}

fun <E> Collection<E>.hasDuplicates(): Boolean {
    return all { item -> count { item == it } == 1 }
}

/** Returns `true` if contains at least one element from `elements` */
fun <T> Iterable<T>.containsAny(elements: Iterable<T>) = any { it in elements }


/**
 * Returns 'true' if the collection contains any of the given [elements]
 */
@JvmName("containsAnyCollection")
fun <T> Collection<T>.containsAny(elements: Collection<T>): Boolean {
    val set = this.toSet()
    val elems = elements.toSet()

    val intersection = set.intersect(elems)

    return intersection.isNotEmpty()
}

/**
 * Returns 'true' if the collection contains any of the given [elements]
 */
@JvmName("containsAnyArray")
fun <T> Collection<T>.containsAny(elements: Array<out T>): Boolean = containsAny(elements.toList())

/**
 * Returns 'true' if the collection contains none of the given [elements]
 */
@JvmName("containsNoneCollection")
fun <T> Collection<T>.containsNone(elements: Collection<T>): Boolean = !containsAny(elements)

/**
 * Returns 'true' if the collection contains none of the given [elements]
 */
@JvmName("containsNoneArray")
fun <T> Collection<T>.containsNone(elements: Array<out T>): Boolean = !containsAny(elements)

fun <T> Collection<T>.isDistinct(): Boolean =
    size == toSet().size

inline fun <T, K> Iterable<T>.isDistinctBy(keySelector: (T) -> K): Boolean =
    map { keySelector(it) }.isDistinct()

/**
 * Similar to [Sequence.all] but it requires at least [min] matching elements to pass.
 */
fun <T> Sequence<T>.all(min: Int, predicate: (T) -> Boolean): Boolean {
    var count = 0
    for (element in this) {
        if (!predicate(element)) return false
        count++
    }

    return count >= min
}